www.pudn.com > vxworks_tcpip_code.rar > if_ppp.c


/* if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver */

/* Copyright 1996 Wind River Systems, Inc. */
#include "copyright_wrs.h"

/*	$NetBSD: if_ppp.c,v 1.20 1994/10/30 21:48:52 cgd Exp $	*/

/*
 * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver.
 *
 * Copyright (c) 1989 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.
 *
 * Drew D. Perkins
 * Carnegie Mellon University
 * 4910 Forbes Ave.
 * Pittsburgh, PA 15213
 * (412) 268-8576
 * ddp@andrew.cmu.edu
 *
 * Based on:
 *	@(#)if_sl.c	7.6.1.2 (Berkeley) 2/15/89
 *
 * Copyright (c) 1987 Regents of the University of California.
 * 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 the University of California, Berkeley.  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.
 *
 * Serial Line interface
 *
 * Rick Adams
 * Center for Seismic Studies
 * 1300 N 17th Street, Suite 1450
 * Arlington, Virginia 22209
 * (703)276-7900
 * rick@seismo.ARPA
 * seismo!rick
 *
 * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
 * Converted to 4.3BSD Beta by Chris Torek.
 * Other changes made at Berkeley, based in part on code by Kirk Smith.
 *
 * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
 * Added VJ tcp header compression; more unified ioctls
 *
 * 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
 * packet doesn't fit in the output ring buffer.
 *
 * Added priority queueing for interactive IP packets, following
 * the model of if_sl.c, plus hooks for bpf.
 * Paul Mackerras (paulus@cs.anu.edu.au).
 */

/* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */

/*
modification history
--------------------
01t,16apr99,koz  Fix for 25165, communications error handling
01s,17feb99,sgv  Fix for spr 23702, pppintr check for NULL ppp_softc
01r,17feb99,sgv  Fix for spr 24459. pppalloc was not setting the correct
		 ppp_softc structure to NULL on error condition
01q.10sep98,sgv  Added fix for interoperating with Windows
01p,18aug97,jmb  Add code to discard packets at interrupt level due
		 to overflow of input ring buffer.
01o,30dec97,sgv  fixed problem in PPP output Hooks
01n,26sep97,sgv  Added PPP output Hooks
01m,19sep97,vin  changed m_ext.ext_buf to m_extBuf and m_ext.ext_size to
		 m_extSize
01l,14nov96,vin  upgraded to use new buffering scheme.
01k,13aug96,vin  upgraded to BSD4.4, removed call in_ifaddr_remove as it is 
		 being done in if_dettach. 
01j,30jul95,dzb  cleanup on errors while openning serial device.
01i,05jul95,dzb  close out serial fd on errors in pppalloc().
01h,12jun95,dzb  added SNMP MIBII support (counters and ioctl).
                 header file consolidation.
01g,24apr95,dzb  removed in_ifaddr_remove() because added to in.c (SPR #4109).
01f,09mar95,dzb  added counters for ip packets sent/received.
01e,06mar95,dzb  changed cfree() to free() (ANSI).
01d,09feb95,dab  added SIOCGIFMTU command to ppptioctl().
01c,07feb95,dzb  changed pppoutput() to return errno, instead of ERROR.
                 removed redundant mbuf check from ppp_wrt_flush().
		 zeroed out m_len after MGET (SPR #4037).
01b,16jan95,dzb  warnings cleanup.  moved global prototypes to if_ppp.h.
01a,21dec94,dab  VxWorks port - first WRS version.
           +dzb  added: path for ppp header files, WRS copyright.
*/

#include "vxWorks.h"
#include "net/mbuf.h"
#include "sockLib.h"
#include "ioctl.h"
#include "stdio.h"
#include "errnoLib.h"
#include "ioLib.h"
#include "iosLib.h"
#include "semLib.h"
#include "memLib.h"
#include "taskLib.h"
#include "signal.h"
#include "sigLib.h"
#include "etherLib.h"
#include "netLib.h"
#include "m2Lib.h"
#include "private/m2LibP.h"
#include "net/if.h"
#include "net/unixLib.h"
#include "net/route.h"
#include "netinet/in.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/ip.h"
#include "pppLib.h"
#include "rngLib.h"
#include "tyLib.h"

#include "tickLib.h"

#if NPPP > 0

#ifdef VJC
#define HDROFF  MAX_HDR
/* HDROFF should really be 128, but other parts of the system will
   panic on TCP+IP headers bigger than MAX_HDR = MHLEN (100). */

#else
#define HDROFF  (0)
#endif

#define PPP_HIWAT       400     /* Don't start a new packet if HIWAT on que */

/* external variables */

IMPORT void ipintr();
IMPORT void if_attach();
IMPORT void if_dettach();
IMPORT void if_down();

/* global variables */

struct ppp_softc *ppp_softc[NPPP] = { 0 } ;

int ppp_wrt_task_priority    	= 60;
int ppp_wrt_task_options     	= VX_SUPERVISOR_MODE | VX_UNBREAKABLE;
int ppp_wrt_task_stack_size   	= 0x4000;

char *ppp_device_name		= "ppp";

static int pppioctl __ARGS((struct ifnet *, int , caddr_t));
static int pppattach __ARGS((int));
static int pppoutput __ARGS((struct ifnet *, struct mbuf *, struct sockaddr *,
			     struct rtentry *));
static void pppinput __ARGS((struct ppp_softc *, int));
static int pppasyncstart __ARGS((struct ppp_softc *));
static u_short pppfcs __ARGS((int, u_char *, int));
static int pppgetm __ARGS((struct ppp_softc *sc));
static void pppdumpm __ARGS((struct mbuf *, int));
static void pppdumpb __ARGS((u_char *, int));
static void ppplogchar __ARGS((struct ppp_softc *, int));
static void pppdealloc __ARGS((struct ppp_softc *));
static struct ppp_softc *pppalloc __ARGS((int, char *));

static void ppp_wrt_flush __ARGS((struct ppp_softc *));
static void pinit __ARGS((int));
static int pppintr __ARGS((int, int));
static void ppp_tty_read __ARGS((int, struct ppp_softc *, int));

/*
 * Some useful mbuf macros not in mbuf.h.
 */
#define M_DATASTART(m)	(m)->m_extBuf

#define M_DATASIZE(m)	(m)->m_extSize

#define PPPBUFSIZE        (PPP_MTU * 2)

/*
 * The following disgusting hack gets around the problem that IP TOS
 * can't be set yet.  We want to put "interactive" traffic on a high
 * priority queue.  To decide if traffic is interactive, we check that
 * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control.
 */
static u_short interactive_ports[8] = {
        0,      513,    0,      0,
        0,      21,     0,      23,
};
#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p))

/*
 * Does c need to be escaped?
 */
#define ESCAPE_P(c)     (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F)))

/*
 * Called to establish ppp interfaces.
 */
static int
pppattach(unit)
    int unit;
{
    register struct ppp_softc *sc;

    sc = ppp_softc[unit];

    sc->sc_if.if_name = "ppp";
    sc->sc_if.if_unit = unit;
    sc->sc_if.if_mtu = PPP_MTU;
    sc->sc_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST ;
    sc->sc_if.if_type = IFT_PPP;
    sc->sc_if.if_hdrlen = PPP_HDRLEN;
    sc->sc_if.if_ioctl = pppioctl;
    sc->sc_if.if_output = pppoutput;
    sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
    sc->sc_inq.ifq_maxlen = IFQ_MAXLEN;
    sc->sc_fastq.ifq_maxlen = IFQ_MAXLEN;
    sc->sc_if.if_reset = pppclose;
    sc->sc_if.if_init = (FUNCPTR) pinit;
    if_attach(&sc->sc_if);

    return (OK);
}

/*
 * Allocate a ppp interface unit and initialize it.
 */
static struct ppp_softc *
pppalloc(unit, device)
    int unit;
    char *device;
{
    struct ppp_softc *sc;

    if (ppp_softc[unit] != NULL)
        return NULL;

    if ((sc = (struct ppp_softc *)calloc(1, sizeof(struct ppp_softc))) == NULL)
        return (NULL);

    ppp_softc[unit] = sc;

    if ((sc->sc_fd = open(device, O_RDWR, 0)) == ERROR)
        {
        ppp_softc[unit] = (struct ppp_softc *)NULL;
        free((char *) sc);
        return NULL;
	}

    if (ioctl(sc->sc_fd, FIORBUFSET, 4 * PPP_MTU) == ERROR ||
        ioctl(sc->sc_fd, FIOWBUFSET, 4 * PPP_MTU) == ERROR) {
        close(sc->sc_fd);
        ppp_softc[unit] = (struct ppp_softc *)NULL;
        free((char *) sc);
        return NULL;
    }

    if (pppattach(unit) == ERROR) { 
        close(sc->sc_fd);
        ppp_softc[unit] = (struct ppp_softc *)NULL;
        free((char *) sc);
        return (NULL);
    }

    if (ioctl(sc->sc_fd, FIOPROTOHOOK, (int)pppintr) == ERROR ||
        ioctl(sc->sc_fd, FIOPROTOARG, unit) == ERROR) {
        close(sc->sc_fd);
        pppdealloc(sc);
        return (NULL);
    }

    sc->sc_flags = 0;
    sc->sc_mru = PPP_MRU;
#ifdef VJC
    sl_compress_init(&sc->sc_comp);
#endif
    sc->sc_if.if_flags |=  IFF_RUNNING;

    return sc;
}

/*
 * Deallocate a ppp unit.
 */
static void
pppdealloc(sc)
    struct ppp_softc *sc;
{
    struct mbuf *m;

    if_down(&sc->sc_if);
    sc->sc_devp = NULL;
    sc->sc_xfer = 0;
    for (;;) {
        IF_DEQUEUE(&sc->sc_inq, m);
        if (m == NULL)
            break;
        m_freem(m);
    }
    for (;;) {
        IF_DEQUEUE(&sc->sc_fastq, m);
        if (m == NULL)
            break;
        m_freem(m);
    }
    sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING);

    if_dettach(&sc->sc_if);

    ppp_softc[sc->sc_if.if_unit] = (struct ppp_softc *)NULL;

    free((char *) sc);
}

/*
 * pppopen - open the ppp interface.
 * 	     Returns unit id.
 */
int
pppopen(unit, device)
    int unit;
    char *device;
{
    register struct ppp_softc *sc;
    char ppp_wrt_name[10];

    if ((sc = pppalloc(unit, device)) == NULL)
        return (ERROR);

    if (sc->sc_outm != NULL) {
        m_freem(sc->sc_outm);
        sc->sc_outm = NULL;
    }

    if (pppgetm(sc) == 0) {
        sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
        return (ERROR);
    }

    sc->sc_ilen  = 0;
    bzero((char *)sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
    sc->sc_asyncmap[0] = 0xffffffff;
    sc->sc_asyncmap[3] = 0x60000000;
    sc->sc_rasyncmap = 0;
    sc->sc_devp = (void *) NULL;
    sc->sc_start = pppasyncstart;
    sc->sc_qlen  = 0;
    sc->sc_tid = taskIdSelf();

    semBInit(&sc->sc_wrtsem, SEM_Q_PRIORITY, SEM_EMPTY);

    ioctl(sc->sc_fd, FIOFLUSH, 0);

    sprintf(ppp_wrt_name, "tPPP%dWrt", sc->sc_if.if_unit);
    sc->sc_wrt_task_id = taskSpawn(ppp_wrt_name, ppp_wrt_task_priority,
                                   ppp_wrt_task_options,
                                   ppp_wrt_task_stack_size,
				   (FUNCPTR)ppp_wrt_task, (int)sc,
				   0, 0, 0, 0, 0, 0, 0, 0, 0);

    if (sc->sc_wrt_task_id == ERROR) {
        close(sc->sc_fd);
        pppdealloc(sc);
        return (ERROR);
    }

    return (OK);
}
    
/*
 * Line specific close routine.
 * Detach the tty from the ppp unit.
 * Mimics part of ttyclose().
 */
int
pppclose(unit)
    int unit;
{
    register struct ppp_softc *sc;
    int s;

    if (unit >= 0 && unit < NPPP)
        sc = ppp_softc[unit];
    else
        return (ERROR);

    if (sc != NULL) {
	ppp_wrt_flush(sc);

        ioctl(sc->sc_fd, FIOPROTOHOOK, NULL);

        taskDelete(sc->sc_wrt_task_id);

        s = splimp();		/* paranoid; splnet probably ok */

        close(sc->sc_fd);

        m_freem(sc->sc_outm);
        sc->sc_outm = NULL;
        m_freem(sc->sc_m);
        sc->sc_m = NULL;
        pppdealloc(sc);

        splx(s);
    }
    else
        return (ERROR);

    return(OK);
}

/*
 * Line specific (tty) read routine.
 */
int
pppread(unit, buf, count)
    int unit;
    char *buf;
    int count;
{
    register struct ppp_softc *sc = ppp_softc[unit];
    struct mbuf *m;
    register int s;

    s = splimp();
    IF_DEQUEUE(&sc->sc_inq, m);
    splx(s);
    if (m == NULL)
	return 0;
    copy_from_mbufs(buf, m, count);
    return (count);
}

/*
 * Line specific (tty) write routine.
 */
int
pppwrite(unit, buf, count)
    int unit;
    char *buf;
    int count;
{
    register struct ppp_softc *sc = ppp_softc[unit];
    struct mbuf *m, *m0, **mp;
    struct sockaddr dst;
    struct ppp_header *ph1, *ph2;
    int len;

    if (count > sc->sc_if.if_mtu + PPP_HDRLEN ||
        count < PPP_HDRLEN) {
        errno = EMSGSIZE;
        return (-1);
    }
    for (mp = &m0; count; mp = &m->m_next) {
        m = mBufClGet(M_WAIT, MT_DATA, count, FALSE);

        if ((*mp = m) == NULL) {
            m_freem(m0);
            errno = ENOBUFS;
            return (-1);
        }
	len = min (count, m->m_extSize); /* num for copy */
        bcopy(buf, mtod(m, caddr_t), len);
        count -= len;
        m->m_len = len;
    }
    dst.sa_family = AF_UNSPEC;
    ph1 = (struct ppp_header *) &dst.sa_data;
    ph2 = mtod(m0, struct ppp_header *);
    memcpy (ph1, ph2, sizeof (*ph1));		/* from RTP for bug on 29k */
    m0->m_data += PPP_HDRLEN;
    m0->m_len -= PPP_HDRLEN;
    return (pppoutput(&sc->sc_if, m0, &dst, (struct rtentry *)0));
}

/*
 * Line specific (tty) ioctl routine.
 * Provide a way to get the ppp unit number.
 * This discipline requires that tty device drivers call
 * the line specific l_ioctl routine from their ioctl routines.
 */
int
ppptioctl(unit, cmd, data)
    int unit;
    int cmd;
    caddr_t data;
{
    register struct ppp_softc *sc = ppp_softc[unit];
    int s, flags, mru;
    int error = 0; 

    if (sc == NULL)
        return -1;

    switch (cmd) {
    case FIONREAD:
        *(int *)data = sc->sc_qlen;
        break;

    case PPPIOCGUNIT:
        *(int *)data = sc->sc_if.if_unit;
        break;

    case PPPIOCGFLAGS:
        *(u_int *)data = sc->sc_flags;
        break;

    case PPPIOCSFLAGS:
        flags = *(int *)data & SC_MASK;
        s = splimp();
        sc->sc_flags = (sc->sc_flags & ~SC_MASK) | flags;
        splx(s);
        break;

    case PPPIOCSASYNCMAP:
        sc->sc_asyncmap[0] = *(u_int *)data;
        break;

    case PPPIOCGASYNCMAP:
        *(u_int *)data = sc->sc_asyncmap[0];
        break;

    case PPPIOCSRASYNCMAP:
        sc->sc_rasyncmap = *(u_int *)data;
        break;

    case PPPIOCGRASYNCMAP:
        *(u_int *)data = sc->sc_rasyncmap;
        break;

    case PPPIOCSXASYNCMAP:
        bcopy((char *)data, (char *)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 */
        break;

    case PPPIOCGXASYNCMAP:
        bcopy((char *)sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap));
        break;

    case PPPIOCSMRU:
        mru = *(int *)data;
        if (mru >= PPP_MRU && mru <= PPP_MAXMRU) {
            sc->sc_mru = mru;
            if (pppgetm(sc) == 0) {
	        error = ENOBUFS;
	        sc->sc_mru = PPP_MRU;
	        if (pppgetm(sc) == 0)
		sc->sc_if.if_flags &= ~IFF_UP;
            }
        }
        break;

    case PPPIOCGMRU:
        *(int *)data = sc->sc_mru;
        break;

#ifdef VJC
    case PPPIOCSMAXCID:
	if (*(int *)data > MAX_STATES)
	    {
	    error = EINVAL; 
	    break;
	    }
        sl_compress_init(&sc->sc_comp);
        break;
#endif

    case PPPIOCXFERUNIT:
        sc->sc_xfer = 0;
        break;

    case SIOCSIFMTU:
	sc->sc_if.if_mtu = *(int *)data;
	break;

    case SIOCGIFMTU:
        *(int *)data = sc->sc_if.if_mtu;
        break;

    case PPPIOCGFD:
        *(int *)data = sc->sc_fd;
        break;

    default:
        return (ENOTSUP);
    }
    return (error);
}

static
void pinit(unit)
    int unit;
{
    struct ppp_softc *ppp = ppp_softc [unit];

    ppp->sc_if.if_flags |= IFF_UP | IFF_RUNNING; /* open for business */
}

/*
 * FCS lookup table as calculated by genfcstab.
 */
static u_short fcstab[256] = {
	0x0000,	0x1189,	0x2312,	0x329b,	0x4624,	0x57ad,	0x6536,	0x74bf,
	0x8c48,	0x9dc1,	0xaf5a,	0xbed3,	0xca6c,	0xdbe5,	0xe97e,	0xf8f7,
	0x1081,	0x0108,	0x3393,	0x221a,	0x56a5,	0x472c,	0x75b7,	0x643e,
	0x9cc9,	0x8d40,	0xbfdb,	0xae52,	0xdaed,	0xcb64,	0xf9ff,	0xe876,
	0x2102,	0x308b,	0x0210,	0x1399,	0x6726,	0x76af,	0x4434,	0x55bd,
	0xad4a,	0xbcc3,	0x8e58,	0x9fd1,	0xeb6e,	0xfae7,	0xc87c,	0xd9f5,
	0x3183,	0x200a,	0x1291,	0x0318,	0x77a7,	0x662e,	0x54b5,	0x453c,
	0xbdcb,	0xac42,	0x9ed9,	0x8f50,	0xfbef,	0xea66,	0xd8fd,	0xc974,
	0x4204,	0x538d,	0x6116,	0x709f,	0x0420,	0x15a9,	0x2732,	0x36bb,
	0xce4c,	0xdfc5,	0xed5e,	0xfcd7,	0x8868,	0x99e1,	0xab7a,	0xbaf3,
	0x5285,	0x430c,	0x7197,	0x601e,	0x14a1,	0x0528,	0x37b3,	0x263a,
	0xdecd,	0xcf44,	0xfddf,	0xec56,	0x98e9,	0x8960,	0xbbfb,	0xaa72,
	0x6306,	0x728f,	0x4014,	0x519d,	0x2522,	0x34ab,	0x0630,	0x17b9,
	0xef4e,	0xfec7,	0xcc5c,	0xddd5,	0xa96a,	0xb8e3,	0x8a78,	0x9bf1,
	0x7387,	0x620e,	0x5095,	0x411c,	0x35a3,	0x242a,	0x16b1,	0x0738,
	0xffcf,	0xee46,	0xdcdd,	0xcd54,	0xb9eb,	0xa862,	0x9af9,	0x8b70,
	0x8408,	0x9581,	0xa71a,	0xb693,	0xc22c,	0xd3a5,	0xe13e,	0xf0b7,
	0x0840,	0x19c9,	0x2b52,	0x3adb,	0x4e64,	0x5fed,	0x6d76,	0x7cff,
	0x9489,	0x8500,	0xb79b,	0xa612,	0xd2ad,	0xc324,	0xf1bf,	0xe036,
	0x18c1,	0x0948,	0x3bd3,	0x2a5a,	0x5ee5,	0x4f6c,	0x7df7,	0x6c7e,
	0xa50a,	0xb483,	0x8618,	0x9791,	0xe32e,	0xf2a7,	0xc03c,	0xd1b5,
	0x2942,	0x38cb,	0x0a50,	0x1bd9,	0x6f66,	0x7eef,	0x4c74,	0x5dfd,
	0xb58b,	0xa402,	0x9699,	0x8710,	0xf3af,	0xe226,	0xd0bd,	0xc134,
	0x39c3,	0x284a,	0x1ad1,	0x0b58,	0x7fe7,	0x6e6e,	0x5cf5,	0x4d7c,
	0xc60c,	0xd785,	0xe51e,	0xf497,	0x8028,	0x91a1,	0xa33a,	0xb2b3,
	0x4a44,	0x5bcd,	0x6956,	0x78df,	0x0c60,	0x1de9,	0x2f72,	0x3efb,
	0xd68d,	0xc704,	0xf59f,	0xe416,	0x90a9,	0x8120,	0xb3bb,	0xa232,
	0x5ac5,	0x4b4c,	0x79d7,	0x685e,	0x1ce1,	0x0d68,	0x3ff3,	0x2e7a,
	0xe70e,	0xf687,	0xc41c,	0xd595,	0xa12a,	0xb0a3,	0x8238,	0x93b1,
	0x6b46,	0x7acf,	0x4854,	0x59dd,	0x2d62,	0x3ceb,	0x0e70,	0x1ff9,
	0xf78f,	0xe606,	0xd49d,	0xc514,	0xb1ab,	0xa022,	0x92b9,	0x8330,
	0x7bc7,	0x6a4e,	0x58d5,	0x495c,	0x3de3,	0x2c6a,	0x1ef1,	0x0f78
};

/*
 * Calculate a new FCS given the current FCS and the new data.
 */
static u_short
pppfcs(fcs, cp, len)
    register u_short fcs;
    register u_char *cp;
    register int len;
{
    while (len--)
        fcs = PPP_FCS(fcs, *cp++);
    return (fcs);
}

/*
 * Queue a packet.  Start transmission if not active.
 * Packet is placed in Information field of PPP frame.
 */
static int
pppoutput(ifp, m0, dst, pRtEntry)
    struct ifnet *ifp;
    struct mbuf *m0;
    struct sockaddr *dst;
    struct rtentry * pRtEntry; 		/* to satisfy compiler */
{
    register struct ppp_softc *sc = ppp_softc[ifp->if_unit];
    struct ppp_header *ph;
    int protocol, address, control;
    u_char *cp;
    int s, error;
    struct ip *ip;
    struct ifqueue *ifq;
    char buf[PPPBUFSIZE];

    if ((sc->sc_fd == 0 || (ifp->if_flags & IFF_RUNNING) == 0
        || (ifp->if_flags & IFF_UP) == 0) && dst->sa_family != AF_UNSPEC) {
        error = ENETDOWN;	/* sort of */
        goto bad;
    }

    /*
     * Compute PPP header.
     */
    address = PPP_ALLSTATIONS;
    control = PPP_UI;
    ifq = &ifp->if_snd;
    switch (dst->sa_family) {
    case AF_INET:
        protocol = PPP_IP;
        if ((sc->sc_flags & SC_ENABLE_IP) == 0) {
            error = ENETDOWN;
            goto bad;
        }

	sc->sc_ipsent++;			/* incr ip packets sent */
        /*
         * If this is a TCP packet to or from an "interactive" port,
         * put the packet on the fastq instead.
         */
        if ((ip = mtod(m0, struct ip *))->ip_p == IPPROTO_TCP) {
            register int p = ntohl(((int *)ip)[ip->ip_hl]);
            if (INTERACTIVE(p & 0xffff) || INTERACTIVE(p >> 16))
                ifq = &sc->sc_fastq;
        }
        break;
#ifdef NS
    case AF_NS:
        protocol = PPP_XNS;
            break;
#endif
    case AF_UNSPEC:
        ph = (struct ppp_header *)dst->sa_data;
        address = ph->ph_address;
        control = ph->ph_control;
        protocol = ntohs(ph->ph_protocol);
        break;
    default:
        printf("ppp%d: af%d not supported\n",
        ifp->if_unit, dst->sa_family);
        error = EAFNOSUPPORT;
        goto bad;
    }

    /*
     * Add PPP header.  If no space in first mbuf, allocate another.
     * (This assumes M_LEADINGSPACE is always 0 for a cluster mbuf.)
     */
    if (M_LEADINGSPACE(m0) < PPP_HDRLEN) {
        m0 = m_prepend(m0, PPP_HDRLEN, M_DONTWAIT);
        if (m0 == 0) {
            error = ENOBUFS;
        goto bad;
        }
        m0->m_len = 0;
    } else
        m0->m_data -= PPP_HDRLEN;

    cp = mtod(m0, u_char *);
    *cp++ = address;
    *cp++ = control;
    *cp++ = protocol >> 8;
    *cp++ = protocol & 0xff;
    m0->m_len += PPP_HDRLEN;

    if (sc->sc_flags & SC_LOG_OUTPKT) {
        printf("ppp%d output: ", ifp->if_unit);
        pppdumpm(m0, -1);
    }

    /* Process etherOutputHook */

    if ((etherOutputHookRtn != NULL))
	{
	struct mbuf *mp;
	int ilen;
	FAST char *p = (char *)buf;

	ilen = 0;

	/* Calculate the packet len */

	for (mp = m0; mp != NULL; mp = mp->m_next)
        ilen += mp->m_len;

	/* Copy the packet to the buffer */

	for (mp = m0; mp != NULL; p += mp->m_len, mp = mp->m_next)
            bcopy(mtod (mp, char *), p, mp->m_len);

	if ((*etherOutputHookRtn) (ifp, buf, ilen) != 0)
            {
	    /* output hook has already processed this packet */
            return (OK);
            }
	}


    /*
     * Put the packet on the appropriate queue.
     */
    s = splimp();
    if (IF_QFULL(ifq)) {
        IF_DROP(ifq);
        splx(s);
        sc->sc_if.if_oerrors++;
        error = ENOBUFS;
        goto bad;
    }
    IF_ENQUEUE(ifq, m0);
    ifp->if_lastchange = tickGet (); 		/* record the last change */

    /*
     * Tell the device to send it out.
     */
    (*sc->sc_start)(sc);

    splx(s);
    return (OK);

bad:
    m_freem(m0);
    errno = error;
    return (error);
}

/*
 * Grab another packet off a queue and apply VJ compression,
 * address/control and/or protocol compression if appropriate.
 */
static struct mbuf *
ppp_dequeue(sc)
    struct ppp_softc *sc;
{
    int s;
    struct mbuf *m;
    u_char *cp;
    int address, control, protocol;

    s = splimp();
    IF_DEQUEUE(&sc->sc_fastq, m);
    if (m == NULL)
        IF_DEQUEUE(&sc->sc_if.if_snd, m);
    splx(s);
    if (m == NULL)
        return NULL;

    /*
     * Extract the ppp header of the new packet.
     * The ppp header will be in one mbuf.
     */
    cp = mtod(m, u_char *);
    address = cp[0];
    control = cp[1];
    protocol = (cp[2] << 8) + cp[3];

    switch (protocol) {
#ifdef VJC
    case PPP_IP:
    /*
     * If the packet is a TCP/IP packet, see if we can compress it.
     */
	if (sc->sc_flags & SC_COMP_TCP) {
            struct ip *ip;
	    int type;
	    struct mbuf *mp;

	    mp = m;
	    ip = (struct ip *) (cp + PPP_HDRLEN);
	    if (mp->m_len <= PPP_HDRLEN) {
                mp = mp->m_next;
		if (mp == NULL)
		    break;
		ip = mtod(mp, struct ip *);
	    }
        /* this code assumes the IP/TCP header is in one non-shared mbuf */
	    if (ip->ip_p == IPPROTO_TCP) {
                type = sl_compress_tcp(mp, ip, &sc->sc_comp,
				       !(sc->sc_flags & SC_NO_TCP_CCID));
		switch (type) {
		case TYPE_UNCOMPRESSED_TCP:
		    protocol = PPP_VJC_UNCOMP;
		    break;
		case TYPE_COMPRESSED_TCP:
		    protocol = PPP_VJC_COMP;
		    cp = mtod(m, u_char *);
		    cp[0] = address;        /* header has moved */
		    cp[1] = control;
		    cp[2] = 0;
		    break;
		}
		cp[3] = protocol;   /* update protocol in PPP header */
	    }
	}
#endif  /* VJC */
    }

    /*
     * Compress the address/control and protocol, if possible.
     */
    if (sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS &&
        control == PPP_UI && protocol != PPP_ALLSTATIONS &&
        protocol != PPP_LCP) {
        /* can compress address/control */
        m->m_data += 2;
        m->m_len -= 2;
    }
    if (sc->sc_flags & SC_COMP_PROT && protocol < 0xFF) {
        /* can compress protocol */
        if (mtod(m, u_char *) == cp) {
            cp[2] = cp[1];      /* move address/control up */
            cp[1] = cp[0];
        }
        ++m->m_data;
        --m->m_len;
    }

    return m;
}

/*
 * This gets called from pppoutput when a new packet is
 * put on a queue.
 */
static int
pppasyncstart(sc)
    register struct ppp_softc *sc;
{
    semGive(&sc->sc_wrtsem);
    return 1;
}

/* 
 * Start output on interface.  Get another datagram
 * to send from the interface queue and map it to
 * the interface before starting output.
 */
void
ppp_wrt_task(sc)
    register struct ppp_softc *sc;
{
    register struct mbuf *m;
    register int len;
    register u_char *start, *stop, *cp;
    int n, ndone, done;
    struct mbuf *m2;
    int count, fd;
    char ch;

    fd = sc->sc_fd;

    for (;;) {
	semTake(&sc->sc_wrtsem, WAIT_FOREVER);

	for (;;) {
            /*
             * See if we have an existing packet partly sent.
             * If not, get a new packet and start sending it.
             * We take packets on the priority queue ahead of those
             * on the normal queue.
             */
            m = sc->sc_outm;
            if (m == NULL) {
                /*
                 * Get another packet to be sent
                 */
                m = ppp_dequeue(sc);
                if (m == NULL)
                    break;

                /*
                 * The extra PPP_FLAG will start up a new packet, and thus
                 * will flush any accumulated garbage.  We do this whenever
                 * the line may have been idle for some time.
                 */
	        ioctl(fd, FIONWRITE, (int) &count);
	        if (count == 0) {
		    ++sc->sc_bytessent;
		    ch = PPP_FLAG;
		    (void) write(fd, &ch, 1);
	        }

                /* Calculate the FCS for the first mbuf's worth. */
                sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len);
            }

	    for (;;) {
		start = mtod(m, u_char *);
		len = m->m_len;
		stop = start + len;
	        while (len > 0) {
                    /*
                     * Find out how many bytes in the string we can
                     * handle without doing something special.
                     */
                    for (cp = start; cp < stop; cp++)
                        if (ESCAPE_P(*cp))
                            break;
                    n = cp - start;
		    if (n) {
                        int cc, nleft;
                        for (nleft = n; nleft > 0; nleft -= cc) {
                            if ((cc =  write(fd, (char *)start, n)) <= 0)
                                break;
                            if (cc > nleft)
                                cc = nleft;
                        }
                        ndone = n - nleft;
                        len -= ndone;
                        start += ndone;
                        sc->sc_bytessent += ndone;

                        if (ndone < n)
                            break;  /* packet doesn't fit */
                    }
		    /*
		     * If there are characters left in the mbuf,
		     * the first one must be special..
		     * Put it out in a different form.
		     */
		    if (len) {
			ch = PPP_ESCAPE;
			if (write(fd, &ch, 1) != 1)
			    break;
    
			ch = *start ^ PPP_TRANS;
			if (write(fd, &ch, 1) != 1)
			    break;
    
			sc->sc_bytessent += 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
                 * flag, and if we can't, leave sc_outm pointing to m, but with
                 * m->m_len == 0, to remind us to output the FCS and flag later.
                 */
                done = len == 0;
                if (done && m->m_next == NULL) {
                    u_char *p;
                    int c;
                    u_char endseq[8];

                    /*
                     * We may have to escape the bytes in the FCS.
                     */
                    p = endseq;
                    c = ~sc->sc_outfcs & 0xFF;
                    if (ESCAPE_P(c)) {
                        *p++ = PPP_ESCAPE;
                        *p++ = c ^ PPP_TRANS;
                    } else
                        *p++ = c;
                    c = (~sc->sc_outfcs >> 8) & 0xFF;
                    if (ESCAPE_P(c)) {
                        *p++ = PPP_ESCAPE;
                        *p++ = c ^ PPP_TRANS;
                    } else
                        *p++ = c;
                    *p++ = PPP_FLAG;

                    /*
                     * Try to output the FCS and flag.  If the bytes
                     * don't all fit, back out.
                     */
                    write(fd, (char *)endseq, (int)p-(int)endseq);
                }

                if (!done) {
		    m->m_data = (caddr_t)start; 
                    m->m_len = len;
                    sc->sc_outm = m;
                    break;         /* can't do any more at the moment */
                }

                /* Finished with this mbuf; free it and move on. */
                m2 = m_free (m);
                if (m2 == NULL)
                    break;

                m = m2;
                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++;
	    sc->sc_if.if_obytes = sc->sc_bytessent;
	}
    }
}

/*
 * Allocate enough mbuf to handle current MRU.
 */
static int
pppgetm(sc)
    register struct ppp_softc *sc;
{
    struct mbuf *m, **mp;
    int len;
    int s;

    s = splimp();
    mp = &sc->sc_m;
    for (len = HDROFF + sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){
	if ((m = *mp) == NULL) {
	    m = mHdrClGet (M_DONTWAIT, MT_DATA, len, FALSE);
	    if (m == NULL)
		break;
	    *mp = m;
	}
	len -= M_DATASIZE(m);
	mp = &m->m_next;
    }
    splx(s);
    return len <= 0;
}

/*
 * pppintr - PPP tty protocol hook routine
 */
static int
pppintr(unit, c)
    int unit;
    int c;
{
    register struct ppp_softc *sc = ppp_softc[unit];

    TY_DEV * pTyDev;
    RING_ID ringId;

    if (ppp_softc[unit] == NULL)
        return FALSE;

    /* Set discard flag if this is a new packet and ring buffer full */

    if ( sc->sc_qlen == 0 )
    {
        pTyDev = (TY_DEV *) iosFdDevFind(sc->sc_fd);
        ringId = pTyDev->rdBuf;
        if (rngIsFull (ringId))
            sc->sc_flags |= SC_DISCARD_PKT;
        else
            sc->sc_flags &= ~SC_DISCARD_PKT;
    }

    ++sc->sc_qlen;

    if (sc->sc_qlen > PPPBUFSIZE)
        {
        netJobAdd((FUNCPTR) ppp_tty_read, (int)unit, (int) sc,
                  (int) sc->sc_qlen, 0, 0);
        sc->sc_qlen = 0;
        return (FALSE);
        }
     
    if (((ppp_if[unit]->lcp_fsm.state) != OPENED) && ((c & 0xff) == 'T'))
        {
        netJobAdd((FUNCPTR) ppp_tty_read, (int)unit, (int) sc,
                  (int) sc->sc_qlen, 0, 0);
        return (FALSE);
        }

    if ((c & 0xff) == PPP_FLAG) 
        {
        netJobAdd((FUNCPTR) ppp_tty_read, (int)unit, (int) sc, 
                      (int) sc->sc_qlen, 0, 0);
        sc->sc_qlen = 0;
        }

    return (FALSE);
}

/*
 * ppp_tty_read - read from ppp tty interface
 */
static void
ppp_tty_read(unit, sc, count)
    int unit;
    register struct ppp_softc *sc;
    register int count;
{
    register int i;
    register int num;
    char buf[PPPBUFSIZE + 2];

    /* count should never be greater than PPPBUFSIZE+1 */

    num = read(sc->sc_fd, buf, count);

    if (count > PPPBUFSIZE)
       {
       /* The received packet is greater than the MTU - Drop the packet */
       sc->sc_if.if_ierrors++;
       return;
       }

    if ((ppp_if[unit]->lcp_fsm.state) != OPENED)
        {
        if (strncmp("CLIENT", buf, strlen("CLIENT") )==0)
           {
           sc->sc_qlen = 0;
           write(sc->sc_fd, "CLIENTSERVER", strlen("CLIENTSERVER"));
           return;
           }
        }

    if (sc->sc_flags & SC_DISCARD_PKT)
        {
        /* Packet discarded */
        sc->sc_if.if_ierrors++;
        sc->sc_flags &= ~SC_DISCARD_PKT;
        return;
        }

    for (i = 0; i < num; i++)
        pppinput(sc, buf[i]);
}

/*
 * PPP packet input routine.
 * The caller has checked and removed the FCS.
 * The return value is 1 if the packet was put on sc->sc_inq,
 * 0 otherwise.
 */
#define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \
                         TYPE_UNCOMPRESSED_TCP)

/*******************************************************************************
* ppppktin - pass the packet to the appropriate stack LCP or IP
* 
* This routine determines packet type and queues the mbuf to the appropriate
* queue. This routine returns a value of 1 if the packet type is other than
* PPP_IP.
*
* NOMANUAL
*
* RETURNS: 0/1
*/

static int
ppppktin(sc, m)
    struct ppp_softc *sc;
    struct mbuf *m;
{
    struct ifqueue *inq;
    int s, ilen, xlen, proto, rv;
    u_char *cp, adrs, ctrl;
    struct mbuf *mp;
    char buf[PPPBUFSIZE];

    sc->sc_if.if_ipackets++;
    sc->sc_if.if_lastchange = tickGet (); 	/* record the last change */
    rv = 0;

    cp = mtod(m, u_char *);
    adrs = cp[0];
    ctrl = cp[1];
    proto = (cp[2] << 8) + cp[3];

    ilen = 0;
    for (mp = m; mp != NULL; mp = mp->m_next)
	ilen += mp->m_len;

#ifdef VJC
    /*
     * See if we have a VJ-compressed packet to uncompress.
     */
    if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
	char *pkttype = proto == PPP_VJC_COMP? "": "un";

	if (sc->sc_flags & SC_REJ_COMP_TCP) {
	    if (sc->sc_flags & SC_DEBUG)
		printf("ppp%d: %scomp pkt w/o compression; flags 0x%x\n",
			sc->sc_if.if_unit, pkttype, sc->sc_flags);
	    m_freem(m);
	    sc->sc_if.if_ierrors++;
	    return 0;
	}

	if (proto == PPP_VJC_COMP && m->m_data - M_DATASTART(m) < MAX_HDR) {
	    /*
	     * We don't have room in the mbuf to decompress this packet.
	     * XXX For now we just drop the packet.
	     */
	    if (sc->sc_flags & SC_DEBUG)
		printf("ppp%d: no room to VJ-decompress packet\n",
		       sc->sc_if.if_unit);
	    m_freem(m);
	    sc->sc_if.if_ierrors++;
	    return 0;
	}

	m->m_data += PPP_HDRLEN;
	m->m_len -= PPP_HDRLEN;
	ilen -= PPP_HDRLEN;
	xlen = sl_uncompress_tcp((u_char **)(&m->m_data), m->m_len,
					  COMPTYPE(proto), &sc->sc_comp);

	if (xlen == 0) {
	    if (sc->sc_flags & SC_DEBUG)
		printf("ppp%d: sl_uncompress failed on type %scomp\n",
			sc->sc_if.if_unit, pkttype);
	    m_freem(m);
	    sc->sc_if.if_ierrors++;
	    return 0;
	}

	/* adjust the first mbuf by the decompressed amt */
	xlen += PPP_HDRLEN;
	m->m_len += xlen - ilen;
	ilen = xlen;
	m->m_data -= PPP_HDRLEN;
	proto = PPP_IP;

	/* put the ppp header back in place */
	if (cp != mtod(m, u_char *)) {
	    cp = mtod(m, u_char *);
	    cp[0] = adrs;
	    cp[1] = ctrl;
	    cp[2] = 0;
	}
	cp[3] = PPP_IP;
    }
#endif /* VJC */

    /*
     * If the packet will fit in a header mbuf, don't waste a
     * whole cluster on it.
     */
    m->m_pkthdr.len = ilen;
    m->m_pkthdr.rcvif = &sc->sc_if;

#if NBPFILTER > 0
    /* See if bpf wants to look at the packet. */
    if (sc->sc_bpf)
	bpf_mtap(sc->sc_bpf, m);
#endif
    
    /* call etherInputHookRtn here */
    if (etherInputHookRtn != NULL) {
        FAST char *p = (char *) buf;
 
	for (mp = m; mp != NULL; p += mp->m_len, mp = mp->m_next)
	    bcopy(mtod (mp, char *), p, mp->m_len);
 
	if ((*etherInputHookRtn) (&sc->sc_if, buf, ilen) != 0)
	    {
	    m_freem (m); 	/* free mbuf, not handed to the upperlayer */
	    return (OK);
	    }
    }

    switch (proto) {
#ifdef INET
    case PPP_IP:
	/*
	 * IP packet - take off the ppp header and pass it up to IP.
	 */
	if ((sc->sc_if.if_flags & IFF_UP) == 0
	    || (sc->sc_flags & SC_ENABLE_IP) == 0) {
	    /* interface is down - drop the packet. */
	    m_freem(m);
	    return 0;
	}
	m->m_pkthdr.len -= PPP_HDRLEN;
	m->m_data += PPP_HDRLEN;
	m->m_len -= PPP_HDRLEN;
	inq = &ipintrq;
	break;
#endif

    default:
	/*
	 * Some other protocol - place on input queue for read().
	 */
	inq = &sc->sc_inq;
	rv = 1;
	break;
    }

    /*
     * Put the packet on the appropriate input queue.
     */
    s = splimp();
    if (IF_QFULL(inq)) {
	IF_DROP(inq);
	if (sc->sc_flags & SC_DEBUG)
	    printf("ppp%d: queue full\n", sc->sc_if.if_unit);
	sc->sc_if.if_ierrors++;
	sc->sc_if.if_iqdrops++;
	m_freem(m);
	rv = 0;
    } else{
        IF_ENQUEUE(inq, m);
	if (proto == PPP_IP) {
	    sc->sc_iprcvd++;		/* incr ip packets received */
	    ipintr();
	} else 
	    kill(sc->sc_tid, SIGIO); 	/* give to alternate processing */
    }
    splx(s);
    return rv;
}

/*
 * tty interface receiver interrupt.
 */
static unsigned paritytab[8] = {
    0x96696996, 0x69969669, 0x69969669, 0x96696996,
    0x69969669, 0x96696996, 0x96696996, 0x69969669
};

static void
pppinput(sc, c)
    struct ppp_softc *sc;
    register int c;
{
    struct mbuf *m;
    int ilen;

    ++sc->sc_bytesrcvd;

    c &= 0xff;

    if (c & 0x80)
	sc->sc_flags |= SC_RCV_B7_1;
    else
	sc->sc_flags |= SC_RCV_B7_0;
    if (paritytab[c >> 5] & (1 << (c & 0x1F)))
	sc->sc_flags |= SC_RCV_ODDP;
    else
	sc->sc_flags |= SC_RCV_EVNP;

    if (sc->sc_flags & SC_LOG_RAWIN)
	ppplogchar(sc, c);

    if (c == PPP_FLAG) {
	ilen = sc->sc_ilen;
	sc->sc_ilen = 0;
	sc->sc_if.if_ibytes = sc->sc_bytesrcvd;

	if (sc->sc_rawin_count > 0) 
	    ppplogchar(sc, -1);

	/*
	 * If SC_ESCAPED is set, then we've seen the packet
	 * abort sequence "}~".
	 */
	if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
	    || ilen > 0 && sc->sc_fcs != PPP_GOODFCS) {
#ifdef VJC
	    /*
	     * If we've missed a packet, we must toss subsequent compressed
	     * packets which don't have an explicit connection ID.
	     */
	    sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &sc->sc_comp);
#endif
	    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);
		sc->sc_if.if_ierrors++;
	    } else
		sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
	    return;
	}

	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);
		sc->sc_if.if_ierrors++;
	    }
	    return;
	}

	/*
	 * Remove FCS trailer.  Somewhat painful...
	 */
	ilen -= 2;
	if (--sc->sc_mc->m_len == 0) {
	    for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next)
		;
	    sc->sc_mc = m;
	}
	sc->sc_mc->m_len--;

	/* excise this mbuf chain */
	m = sc->sc_m;
	sc->sc_m = sc->sc_mc->m_next;
	sc->sc_mc->m_next = NULL;

	if (sc->sc_flags & SC_LOG_INPKT) {
	    printf("ppp%d: got %d bytes\n", sc->sc_if.if_unit, ilen);
	    pppdumpm(m, ilen);
	}
	
	ppppktin(sc, m); 		/* handover packet to upper layer */
	pppgetm(sc);			
	return;
    }

    if (sc->sc_flags & SC_FLUSH) {
	if (sc->sc_flags & SC_LOG_FLUSH)
	    ppplogchar(sc, c);
	return;
    }

    if (c < 0x20 && (sc->sc_rasyncmap & (1 << c)))
	return;

    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;
    }

    /*
     * Initialize buffer on first octet received.
     * First octet could be address or protocol (when compressing
     * address/control).
     * Second octet is control.
     * Third octet is first or second (when compressing protocol)
     * octet of protocol.
     * Fourth octet is second octet of protocol.
     */
    if (sc->sc_ilen == 0) {
	/* reset the first input mbuf */
	if (sc->sc_m == NULL) {
	    pppgetm(sc);
	    if (sc->sc_m == NULL) {
		if (sc->sc_flags & SC_DEBUG)
		    printf("ppp%d: no input mbufs!\n", sc->sc_if.if_unit);
		goto flush;
	    }
	}
	m = sc->sc_m;
	m->m_len = 0;
	m->m_data = M_DATASTART(sc->sc_m);
	if (M_DATASIZE(sc->sc_m) >= HDROFF + PPP_HDRLEN)
	    m->m_data += HDROFF;	/* allow room for VJ decompression */
	sc->sc_mc = m;
	sc->sc_mp = mtod(m, char *);
	sc->sc_fcs = PPP_INITFCS;
	if (c != PPP_ALLSTATIONS) {
	    if (sc->sc_flags & SC_REJ_COMP_AC) {
		if (sc->sc_flags & SC_DEBUG)
		    printf("ppp%d: garbage received: 0x%x (need 0xFF)\n",
			   sc->sc_if.if_unit, c);
		goto flush;
	    }
	    *sc->sc_mp++ = PPP_ALLSTATIONS;
	    *sc->sc_mp++ = PPP_UI;
	    sc->sc_ilen += 2;
	    m->m_len += 2;
	}
    }
    if (sc->sc_ilen == 1 && c != PPP_UI) {
	if (sc->sc_flags & SC_DEBUG)
	    printf("ppp%d: missing UI (0x3), got 0x%x\n",
		   sc->sc_if.if_unit, c);
	goto flush;
    }
    if (sc->sc_ilen == 2 && (c & 1) == 1) {
	/* a compressed protocol */
	*sc->sc_mp++ = 0;
	sc->sc_ilen++;
	sc->sc_mc->m_len++;
    }
    if (sc->sc_ilen == 3 && (c & 1) == 0) {
	if (sc->sc_flags & SC_DEBUG)
	    printf("ppp%d: bad protocol %x\n", sc->sc_if.if_unit,
		   (sc->sc_mp[-1] << 8) + c);
	goto flush;
    }

    /* packet beyond configured mru? */
    if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) {
	if (sc->sc_flags & SC_DEBUG)
	    printf("ppp%d: packet too big\n", sc->sc_if.if_unit);
	goto flush;
    }

    /* is this mbuf full? */
    m = sc->sc_mc;
    if (M_TRAILINGSPACE(m) <= 0) {
	if (m->m_next == NULL) {
	    pppgetm(sc);
	    if (m->m_next == NULL) {
		if (sc->sc_flags & SC_DEBUG)
		    printf("ppp%d: too few input mbufs!\n", sc->sc_if.if_unit);
		goto flush;
	    }
	}
	sc->sc_mc = m = m->m_next;
	m->m_len = 0;
	m->m_data = M_DATASTART(m);
	sc->sc_mp = mtod(m, char *);
    }

    ++m->m_len;
    *sc->sc_mp++ = c;
    sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
    return;

 flush:
    if (!(sc->sc_flags & SC_FLUSH)) {
	sc->sc_if.if_ierrors++;
	sc->sc_flags |= SC_FLUSH;
	if (sc->sc_flags & SC_LOG_FLUSH)
	    ppplogchar(sc, c);
    }
}

/*
 * Process an ioctl request to interface.
 */
static int
pppioctl(ifp, cmd, data)
    register struct ifnet *ifp;
    int cmd;
    caddr_t data;
{
    register struct ppp_softc *sc = ppp_softc[ifp->if_unit];
    register struct ifaddr *ifa = (struct ifaddr *)data;
    register struct ifreq *ifr = (struct ifreq *)data;
    register M2_NETDRVCNFG *pm2DrvCnfg = (M2_NETDRVCNFG *)data;
    register M2_NETDRVCNTRS *pm2DrvCntr = (M2_NETDRVCNTRS *)data;
    int s = splimp(), error = 0;

    switch (cmd) {
    case SIOCGIFFLAGS:
        *(short *)data = ifp->if_flags;
         break;

    case SIOCSIFFLAGS:
	if ((ifp->if_flags & IFF_RUNNING) == 0)
	    ifp->if_flags &= ~IFF_UP;
	break;

    case SIOCSIFADDR:
	if (ifa->ifa_addr->sa_family != AF_INET)
	    error = EAFNOSUPPORT;
	break;

    case SIOCSIFDSTADDR:
	if (ifa->ifa_dstaddr->sa_family != AF_INET)
	    error = EAFNOSUPPORT;
	break;

    case SIOCSIFMTU:
	sc->sc_if.if_mtu = ifr->ifr_mtu;
	break;

    case SIOCGIFMTU:
	ifr->ifr_mtu = sc->sc_if.if_mtu;
	break;

    case SIOCADDMULTI:
    case SIOCDELMULTI:
        if (ifr == 0) {
            error = EAFNOSUPPORT;
            break;
        }
        switch(ifr->ifr_addr.sa_family) {
#ifdef INET
        case AF_INET:
            break;
#endif
        default:
            error = EAFNOSUPPORT;
            break;
        }
        break;

    case SIOCGMIB2CNFG:
        pm2DrvCnfg->ifType = M2_ifType_ppp;		/* ppp type */
        pm2DrvCnfg->ifSpecific.idLength = 0;		/* use default */
	break;

    case SIOCGMIB2CNTRS:				/* fill in counters */
	pm2DrvCntr->ifInOctets = sc->sc_bytesrcvd;
        pm2DrvCntr->ifInNUcastPkts = 0;
        pm2DrvCntr->ifInDiscards = 0;
        pm2DrvCntr->ifInUnknownProtos = ppp_if[ppp_unit]->unknownProto;
	pm2DrvCntr->ifOutOctets = sc->sc_bytessent;
        pm2DrvCntr->ifOutNUcastPkts = 0;
        pm2DrvCntr->ifOutDiscards = 0;

	if ((pm2DrvCntr->ifSpeed = ppp_if[ppp_unit]->baud_rate) == 0)
	    pm2DrvCntr->ifSpeed = 9600;			/* nominal default */

	break;

    default:
	error = EINVAL;
    }
    splx(s);
    return (error);
}

#define	MAX_DUMP_BYTES	128

static void
pppdumpm(m0, pktlen)
    struct mbuf *m0;
    int pktlen;
{
    char buf [3*MAX_DUMP_BYTES+4];
    char *bp = buf;
    struct mbuf *m;
    static char digits [] = "0123456789abcdef";

    for (m = m0; m && pktlen; m = m->m_next) {
	int l = m->m_len;
	u_char *rptr = mtod(m, u_char *);

        if (pktlen > 0) {
            if (l > pktlen)
                l = pktlen;
	    pktlen -= l;
	}
        while (l--) {
            if (bp > buf + sizeof(buf) - 4)
                goto done;
            *bp++ = digits[*rptr >> 4]; /* convert byte to ascii hex */
            *bp++ = digits[*rptr++ & 0xf];
        }

        if (m->m_next) {
            if (bp > buf + sizeof(buf) - 3)
                goto done;
            *bp++ = '|';
        } else
            *bp++ = ' ';
    }
done:
    if (m && pktlen)
        *bp++ = '>';
    *bp = 0;
    printf("%s\n", buf);
}

static void
ppplogchar(sc, c)
    struct ppp_softc *sc;
    int c;
{
    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)) {
        printf("ppp%d input: ", sc->sc_if.if_unit);
        pppdumpb(sc->sc_rawin, sc->sc_rawin_count);
        sc->sc_rawin_count = 0;
    }
}

static void
pppdumpb(b, l)
    u_char *b;
    int l;
{
    char buf [3*MAX_DUMP_BYTES+4];
    char *bp = buf;
    static char digits[] = "0123456789abcdef";

    while (l--) {
	if (bp >= buf + sizeof (buf) - 3) {
	    *bp++ = '>';
	    break;
	}
	*bp++ = digits [*b >> 4]; /* convert byte to ascii hex */
	*bp++ = digits [*b++ & 0xf];
        *bp++ = ' ';
    }

    *bp = 0;
    printf("%s\n", buf);
}

/* 
 * Flush write output on the interface. 
 */
static void
ppp_wrt_flush(sc)
    register struct ppp_softc *sc;
{
    register struct mbuf *m;
    register int len;
    register u_char *start, *stop, *cp;
    int n, ndone, done;
    struct mbuf *m2;
    int count, fd;
    char ch;

    fd = sc->sc_fd;

    for (;;) {
        /*
         * See if we have an existing packet partly sent.
         * If not, get a new packet and start sending it.
         * We take packets on the priority queue ahead of those
         * on the normal queue.
         */
        m = sc->sc_outm;
        if (m == NULL) {
	    /*
	     * Get another packet to be sent
	     */
	    m = ppp_dequeue(sc);
	    if (m == NULL)
	        break;

	    /*
	     * The extra PPP_FLAG will start up a new packet, and thus
	     * will flush any accumulated garbage.  We do this whenever
	     * the line may have been idle for some time.
	     */
	    ioctl(fd, FIONWRITE, (int) &count);
	    if (count == 0) {
	        ++sc->sc_bytessent;
	        ch = PPP_FLAG;
	        (void) write(fd, &ch, 1);
	    }

	    /* Calculate the FCS for the first mbuf's worth. */
	    sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len);
        }

        for (;;) {
	    start = mtod(m, u_char *);
	    len = m->m_len;
	    stop = start + len;
	    while (len > 0) {
	        /*
	         * Find out how many bytes in the string we can
	         * handle without doing something special.
	         */
	        for (cp = start; cp < stop; cp++)
		    if (ESCAPE_P(*cp))
		        break;
	        n = cp - start;
	        if (n) {
		    int cc, nleft;
		    for (nleft = n; nleft > 0; nleft -= cc) {
		        if ((cc =  write(fd, (char *)start, n)) <= 0)
			    break;
		        if (cc > nleft)
			    cc = nleft;
		    }
		    ndone = n - nleft;
		    len -= ndone;
		    start += ndone;
		    sc->sc_bytessent += ndone;

		    if (ndone < n)
		        break;  /* packet doesn't fit */
	        }
	        /*
	         * If there are characters left in the mbuf,
	         * the first one must be special..
	         * Put it out in a different form.
	         */
	        if (len) {
		    ch = PPP_ESCAPE;
		    if (write(fd, &ch, 1) != 1)
		        break;

		    ch = *start ^ PPP_TRANS;
		    if (write(fd, &ch, 1) != 1)
		        break;

		    sc->sc_bytessent += 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
	     * flag, and if we can't, leave sc_outm pointing to m, but with
	     * m->m_len == 0, to remind us to output the FCS and flag later.
	     */
	    done = len == 0;
	    if (done && m->m_next == NULL) {
	        u_char *p;
	        int c;
	        u_char endseq[8];
    
	        /*
	         * We may have to escape the bytes in the FCS.
	         */
	        p = endseq;
	        c = ~sc->sc_outfcs & 0xFF;
	        if (ESCAPE_P(c)) {
		    *p++ = PPP_ESCAPE;
		    *p++ = c ^ PPP_TRANS;
	        } else
		    *p++ = c;
	        c = (~sc->sc_outfcs >> 8) & 0xFF;
	        if (ESCAPE_P(c)) {
		    *p++ = PPP_ESCAPE;
		    *p++ = c ^ PPP_TRANS;
	        } else
		    *p++ = c;
	        *p++ = PPP_FLAG;

	        /*
	         * Try to output the FCS and flag.  If the bytes
	         * don't all fit, back out.
	         */
	        write(fd, (char *)endseq, (int)p-(int)endseq);
	    }

	    if (!done) {
	        m->m_data = (caddr_t)start; 
	        m->m_len = len;
	        sc->sc_outm = m;
	        break;         /* can't do any more at the moment */
	    }

	    /* Finished with this mbuf; free it and move on. */
	    m2 = m_free (m);

	    if (m2 == NULL)
	        break;

	    m = m2;
	    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++;
    }
}
#endif  /* NPPP > 0 */